package com.ruoyi.system.service.impl;

import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson2.JSON;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.ruoyi.common.core.enums.PaymentStatusEnum;
import com.ruoyi.system.domain.WxPayProperties;
import com.ruoyi.system.domain.WxPayTwoProperties;
import com.ruoyi.system.domain.bm.BMPayment;
import com.ruoyi.system.mapper.bm.BMPaymentMapper;
import com.ruoyi.system.service.BaseWxPayService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Objects;

import static com.ruoyi.common.core.constant.WxPayConstant.WX_REFUND_OUT_TRADE_PREFIX;
import static com.ruoyi.system.service.impl.BaseWxPayCallbackServiceImpl.parseISO8601ToLocalDateTime;

/**
 * BaseWxMpServiceImpl
 *
 **/
@Slf4j
@AllArgsConstructor
@Service
public class BaseWxPayServiceImpl implements BaseWxPayService {

    private final WxPayProperties properties;
    private final WxPayTwoProperties twoProperties;

    private final BMPaymentMapper paymentMapper;

//    @Override
    public WxPayService getWxPayService(Integer indexId) {
        log.info("初始化微信支付配置");
        indexId = Objects.nonNull(indexId) ? indexId : 1;
        String appId;
        String mchId;
        String mchKey;
        String keyPath;
        String apiV3Key;
        String certSerialNo;
        String privateCertPath;
        String privateKeyPath;
        String publicKeyPath;
        String publicKeyId;
        switch(indexId){
            case 2:
                appId = twoProperties.getAppId();
                mchId = twoProperties.getMchId();
                mchKey = twoProperties.getMchKey();
                keyPath = twoProperties.getKeyPath();
                apiV3Key = twoProperties.getApiV3Key();
                certSerialNo = twoProperties.getCertSerialNo();
                privateCertPath = twoProperties.getPrivateCertPath();
                privateKeyPath = twoProperties.getPrivateKeyPath();
                publicKeyPath = twoProperties.getPublicKeyPath();
                publicKeyId = twoProperties.getPublicKeyId();
                break;
            default:
                appId = properties.getAppId();
                mchId = properties.getMchId();
                mchKey = properties.getMchKey();
                keyPath = properties.getKeyPath();
                apiV3Key = properties.getApiV3Key();
                certSerialNo = properties.getCertSerialNo();
                privateCertPath = properties.getPrivateCertPath();
                privateKeyPath = properties.getPrivateKeyPath();
                publicKeyPath = properties.getPublicKeyPath();
                publicKeyId = properties.getPublicKeyId();
                break;
        }
        // 初始化配置
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(StringUtils.trimToNull(appId));
        payConfig.setMchId(StringUtils.trimToNull(mchId));
        payConfig.setMchKey(StringUtils.trimToNull(mchKey));
        payConfig.setKeyPath(StringUtils.trimToNull(keyPath));
        payConfig.setApiV3Key(StringUtils.trimToNull(apiV3Key));
        payConfig.setCertSerialNo(StringUtils.trimToNull(certSerialNo));
        payConfig.setPrivateCertPath(StringUtils.trimToNull(privateCertPath));
        payConfig.setPrivateKeyPath(StringUtils.trimToNull(privateKeyPath));
        payConfig.setPublicKeyPath(StringUtils.trimToNull(publicKeyPath));
        payConfig.setPublicKeyId(StringUtils.trimToNull(publicKeyId));


        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }

    @Override
    public WxPayUnifiedOrderV3Result.JsapiResult createWxPayUnifiedOrder(Long paymentId) {
        BMPayment payment = paymentMapper.selectById(paymentId);


        String appId = this.properties.getAppId();
        String mchId = this.properties.getMchId();
        String payNotifyUrl = this.properties.getPayNotifyUrl();
        if (Objects.equals(payment.getIndexId(),1)){
            appId = properties.getAppId();
            mchId = properties.getMchId();
            payNotifyUrl = properties.getPayNotifyUrl() + "/" + payment.getIndexId();
        } else if (Objects.equals(payment.getIndexId(),2)){
            appId = twoProperties.getAppId();
            mchId = twoProperties.getMchId();
            payNotifyUrl = twoProperties.getPayNotifyUrl() + "/" + payment.getIndexId();
        }
        log.info("微信支付下单url" +  payNotifyUrl);
        // 调用微信支付接口生成预支付订单
        WxPayUnifiedOrderV3Request orderRequest = new WxPayUnifiedOrderV3Request();
        orderRequest.setAppid(appId);
        orderRequest.setMchid(mchId);
        orderRequest.setDescription(payment.getBody());
        orderRequest.setOutTradeNo(payment.getOutOrderSn());
        orderRequest.setTimeExpire(getTimeExpire());
        //这里拼接参数 试试
        orderRequest.setNotifyUrl(payNotifyUrl); // 支付结果通知URL

        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        //乘以一百
        amount.setTotal(NumberUtil.mul(payment.getAmount(), 100).intValue());
        amount.setCurrency("CNY");
        orderRequest.setAmount(amount);

        WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
        payer.setOpenid(payment.getOpenId());
        orderRequest.setPayer(payer);
        WxPayUnifiedOrderV3Result.JsapiResult wxOrder;
        try {
            wxOrder = getWxPayService(payment.getIndexId()).createOrderV3(TradeTypeEnum.JSAPI,orderRequest);
            log.info("商品信息：{}，微信支付下单成功, wxOrder: {}", payment.getBody(),  JSON.toJSONString(wxOrder));
        } catch (Exception e) {
            log.error(" 商品信息：{}，微信支付下单失败: {}", payment.getBody(), e.getMessage(), e);
            throw new RuntimeException("微信支付下单失败");
        }
        return wxOrder;
    }

    @Override
    public WxPayRefundV3Result applyWxRefund(Long paymentId) {
        BMPayment payment = paymentMapper.selectById(paymentId);
        if (Objects.nonNull(payment) && !Objects.equals(payment.getPaymentStatus(), PaymentStatusEnum.PAID.getId())){
            log.info("订单支付状态,{}", PaymentStatusEnum.isExistById(payment.getPaymentStatus()).getName());
            throw new RuntimeException("订单支付状态异常");
        }
        if (Objects.isNull( payment.getAmount()) || payment.getAmount().compareTo(BigDecimal.ZERO) <= 0){
            return null;
        }
        // 微信退款请求

        WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
        wxPayRefundV3Request.setTransactionId(payment.getTransactionNo());
        wxPayRefundV3Request.setOutTradeNo(payment.getOutOrderSn());
        String outRefundNo = WX_REFUND_OUT_TRADE_PREFIX + ObjectId.next();
        wxPayRefundV3Request.setOutRefundNo(outRefundNo);
        wxPayRefundV3Request.setReason("审核不通过");
        wxPayRefundV3Request.setNotifyUrl(properties.getRefundNotifyUrl() + "/" + payment.getIndexId());

        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        amount.setRefund(NumberUtil.mul(payment.getAmount(), 100).intValue());
        amount.setCurrency("CNY");
        amount.setTotal(NumberUtil.mul(payment.getAmount(), 100).intValue());
        wxPayRefundV3Request.setAmount(amount);
//        wxPayRefundV3Request.setSubMchid(this.properties.getMchId());


        WxPayRefundV3Result wxPayRefundResult;
        try {
            wxPayRefundResult = getWxPayService(payment.getIndexId()).refundV3(wxPayRefundV3Request);
            log.info("微信申请退款结果:, {}", JSON.toJSONString(wxPayRefundResult));
            // 判断退款信息是否正确
            if ("SUCCESS".equals(wxPayRefundResult.getStatus()) || "PROCESSING".equals(wxPayRefundResult.getStatus())) {
                payment.setRefundTime(parseISO8601ToLocalDateTime(wxPayRefundResult.getCreateTime()));
                payment.setRefundTradeId(wxPayRefundResult.getRefundId());
                payment.setRefundAmount(BigDecimal.valueOf(wxPayRefundResult.getAmount().getRefund()).divide(BigDecimal.valueOf(100)));
                payment.setPaymentStatus(PaymentStatusEnum.APPLY_REFUNDING.getId());
                paymentMapper.updateById(payment);
                return wxPayRefundResult;
            } else {
                throw new RuntimeException("微信申请退款结果失败");
            }
        } catch (WxPayException e) {
            log.error("微信申请退款结果异常, {}", e.getMessage(), e);
            throw new RuntimeException("微信申请退款结果失败");
        }
    }

    @Override
    public WxPayNotifyV3Result payNotifyCallback(String notifyData, SignatureHeader header,Integer indexId) throws WxPayException {
        return getWxPayService(indexId).parseOrderNotifyV3Result(notifyData, header);
    }

    @Override
    public WxPayRefundNotifyV3Result refundNotifyCallback(String notifyData, SignatureHeader header,Integer indexId) throws WxPayException {
        return getWxPayService(indexId).parseRefundNotifyV3Result(notifyData, header);
    }

    /**
     * 获取当前时间+30分钟，并格式化为RFC3339标准格式
     * @return 格式化后的时间字符串，例如：2023-10-01T12:34:56+08:00
     */
    public static String getTimeExpire() {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();

        // 增加30分钟
        LocalDateTime expireTime = now.plusMinutes(30);

        // 将LocalDateTime转换为带时区的OffsetDateTime（使用东八区，即北京时间）
        OffsetDateTime offsetDateTime = expireTime.atOffset(ZoneOffset.ofHours(8));

        // 格式化为RFC3339标准格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
        String timeExpire = offsetDateTime.format(formatter);

        return timeExpire;
    }
}
